import {Layout} from '../../src/Layout';
import {InstallCommand} from '../../src/InstallCommand';
export default Layout;

import {Disclosure, DisclosureTitle, DisclosurePanel} from '@react-spectrum/s2';

export const section = 'Guides';
export const description = 'How to style React Aria components.';

# Styling

<PageDescription>React Aria does not include any styles by default. Learn how to build custom designs to fit your application or design system using any styling solution.</PageDescription>

## Class names

Each component accepts the standard `className` and `style` props which enable using vanilla CSS, utility classes (e.g. Tailwind), CSS-in-JS (e.g. Styled Components), etc.

When a custom `className` is not provided, each component includes a default class name following the `react-aria-ComponentName` naming convention. You can use this to style a component with standard CSS without needing any custom classes.

```css
.react-aria-Select {
  /* ... */
}
```

A custom `className` can also be specified on any component. This overrides the default `className` provided by React Aria with your own.

```jsx
<Select className="my-select">
  {/* ... */}
</Select>
```

## States

React Aria exposes UI states such as pressed, hovered, and selected using data attributes, which are like custom pseudo classes.

```css
.react-aria-ListBoxItem[data-selected] {
  /* ... */
}

.react-aria-ListBoxItem[data-focused] {
  /* ... */
}
```

React Aria includes states such as `data-hovered` and `data-pressed` which are similar to CSS pseudo classes such as `:hover` and `:active`, but work consistently between mouse, touch, and keyboard modalities. You can read more about this in our [blog post series](blog/building-a-button-part-1) and our [Interactions](quality#interactions) overview.

## Render props

The `className` and `style` props also accept functions which receive states for styling. This lets you dynamically determine the classes or styles to apply.

```jsx
<ListBoxItem className={({isSelected}) => isSelected ? 'selected' : 'unselected'}>
  Item
</ListBoxItem>
```

Render props may also be used as children to alter what elements are rendered based on the current state. For example, you could render a checkmark icon when an item is selected.

```jsx
<ListBoxItem>
  {({isSelected}) => (
    <>
      {isSelected && <CheckmarkIcon />}
      <span>Item</span>
    </>
  )}
</ListBoxItem>
```

Render props also let you modify the default values provided by React Aria via the `defaultClassName`, `defaultStyle`, and `defaultChildren` options. For example, you could wrap the default children of a `SelectValue` in an extra element, append an additional class name to React Aria's default, or merge default inline styles with your own.

```jsx
<SelectValue>
  {({defaultChildren}) => <span>{defaultChildren}</span>}
</SelectValue>
```

## Slots

Some patterns include multiple instances of the same component, for example the increment and decrement buttons in a [NumberField](NumberField). These are distinguished by the `slot` prop, which can also be used in CSS for styling purposes.

```tsx
<NumberField>
  <Label>Width</Label>
  <Group>
    <Input />
    {/*- begin highlight -*/}
    <Button slot="increment">+</Button>
    <Button slot="decrement">-</Button>
    {/*- end highlight -*/}
  </Group>
</NumberField>
```

```css
.react-aria-NumberField {
  /*- begin highlight -*/
  [slot=increment] {
  /*- end highlight -*/
    border-radius: 4px 4px 0 0;
  }

  /*- begin highlight -*/
  [slot=decrement] {
  /*- end highlight -*/
    border-radius: 0 0 4px 4px;
  }
}
```

## CSS variables

Some components provide CSS variables that you can use in your styling code. For example, [Popover](Popover) provides a `--trigger-width` variable, which can be used to make the width of the popover match the width of its trigger.

```css
.react-aria-Popover {
  width: var(--trigger-width);
}
```

## Tailwind CSS

When using Tailwind, use [data attributes](https://tailwindcss.com/docs/hover-focus-and-other-states#data-attributes) as modifiers:

```jsx
<ListBoxItem className="data-[selected]:bg-blue-400 data-[disabled]:bg-gray-100">
  Item
</ListBoxItem>
```

Alternatively, you can use [render props](#render-props) to control which Tailwind classes are applied based on states. This can be useful if you need to apply multiple classes based on a single state:

```jsx
<Radio
  className={({isFocusVisible, isSelected}) => `
    flex rounded-lg p-4
    ${isFocusVisible ? 'ring-2 ring-blue-600 ring-offset-1' : ''}
    ${isSelected ? 'bg-blue-600 border-white/30 text-white' : ''}
  `}>
  {/* ... */}
</Radio>
```

To access [CSS variables](#css-variables), use Tailwind's [arbitrary value](https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values) syntax.

```jsx
<Popover className="w-(--trigger-width)">
  {/* ... */}
</Popover>
```

### Plugin

A Tailwind CSS plugin is also available to make styling states of React Aria Components easier, with shorter names and autocomplete in your editor. To install:

<InstallCommand pkg="tailwindcss-react-aria-components" />

When using Tailwind v4, add it to your CSS:

```css
@import "tailwindcss";
@plugin "tailwindcss-react-aria-components";
```

<Disclosure size="S" isQuiet>
  <DisclosureTitle>Tailwind v3</DisclosureTitle>
  <DisclosurePanel>
When using Tailwind v3, install `tailwindcss-react-aria-components` version 1.x instead of 2.x, and add the plugin to your `tailwind.config.js` instead:

```tsx
/** @type {import('tailwindcss').Config} */
module.exports = {
  // ...
  plugins: [
    require('tailwindcss-react-aria-components')
  ]
};
```
  </DisclosurePanel>
</Disclosure>

With the plugin installed, you can access all states without the `data-` prefix. If you have the [Tailwind VSCode Extension](https://tailwindcss.com/docs/editor-setup#intelli-sense-for-vs-code) installed, you'll also get autocomplete for all states in your editor.

```jsx
<ListBoxItem className="selected:bg-blue-400 disabled:bg-gray-100">
  Item
</ListBoxItem>
```

### Boolean states

Boolean states such as `data-pressed` can be styled with `pressed:` like this:

```jsx
<Button className="pressed:bg-blue">
  {/* ... */}
</Button>
```

### Non-boolean states

Non-boolean states follow the `{name}-{value}` pattern. For example, an element with `data-orientation="vertical"` can be styled using `orientation-vertical:`.

```jsx
<Tabs className="orientation-vertical:flex-row">
  {/* ... */}
</Tabs>
```

### Modifier prefix

By default, all modifiers are unprefixed (e.g. `disabled:`), and generate CSS that automatically handles both React Aria Components and native CSS pseudo classes when the names conflict. If you prefer, you can optionally prefix all React Aria Components modifiers with a string of your choice.

```css
@plugin "tailwindcss-react-aria-components" { prefix: rac };
```

<Disclosure size="S" isQuiet>
  <DisclosureTitle>Tailwind v3</DisclosureTitle>
  <DisclosurePanel>
When using Tailwind v3, pass the prefix option to the plugin in `tailwind.config.js`:

```jsx
/** @type {import('tailwindcss').Config} */
module.exports = {
  plugins: [
    require('tailwindcss-react-aria-components')({prefix: 'rac'})
  ],
};
```
  </DisclosurePanel>
</Disclosure>

With this configured, all states for React Aria Components can be accessed with that prefix.

```jsx
<ListBoxItem className="rac-selected:bg-blue-400 rac-disabled:bg-gray-100">
  Item
</ListBoxItem>
```

## Animation

React Aria Components supports both [CSS transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions/Using_CSS_transitions) and [keyframe animations](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes), and works with JavaScript animation libraries like [Motion](https://motion.dev/).

### CSS transitions

Several components support entry and exit animations via the `data-entering` and `data-exiting` states, or via the corresponding render prop functions.

* `data-entering` represents the starting state of the entry animation. The component will transition from the entering state to the default state when it opens.
* `data-exiting` represents the ending state of the exit animation. The component will transition from the default state to the exiting state and wait for any animations to complete before being removed from the DOM.

```css
.react-aria-Popover {
  transition: opacity 300ms;

  &[data-entering],
  &[data-exiting] {
    opacity: 0;
  }
}
```

Note that the `data-entering` state is only applied for one frame when using CSS transitions. The transition itself should be assigned in the default state. To create a different exit animation, assign the transition in the `data-exiting` state.

```css
.react-aria-Popover {
  /* entry transition */
  transition: transform 300ms, opacity 300ms;

  /* starting state of the entry transition */
  &[data-entering] {
    opacity: 0;
    transform: scale(0.8);
  }

  &[data-exiting] {
    /* exit transition */
    transition: opacity 150ms;
    /* ending state of the exit transition */
    opacity: 0;
  }
}
```

### CSS animations

For more complex animations, you can also apply CSS keyframe animations using the same `data-entering` and `data-exiting` states.

```css
.react-aria-Popover[data-entering] {
  animation: slide 300ms;
}

.react-aria-Popover[data-exiting] {
  animation: slide 300ms reverse;
}

@keyframes slide {
  from {
    transform: translateY(-20px);
    opacity: 0;
  }

  to {
    transform: translateY(0);
    opacity: 1;
  }
}
```

Note that unlike CSS transitions, keyframe animations are not interruptible. If the user opens and closes an overlay quickly, the animation may appear to jump to the ending state before the next animation starts.

### Tailwind

If you are using Tailwind, we recommend using the [tailwindcss-animate](https://github.com/jamiebuilds/tailwindcss-animate) plugin. This includes utilities for building common animations such as fading, sliding, and zooming.

```jsx
<Popover className="data-[entering]:animate-in data-[entering]:fade-in data-[exiting]:animate-out data-[exiting]:fade-out">
  {/* ... */}
</Popover>
```

### Motion

[Motion](https://motion.dev) and other JavaScript animation libraries can also be used with React Aria Components. Use [motion.create](https://motion.dev/docs/react-motion-component#custom-components) to create a wrapper component that adds support for Motion's animation props.

```tsx
import {Modal, ModalOverlay} from 'react-aria-components';
import {motion} from 'motion/react';

// Create Motion wrappers.
const MotionModal = motion.create(Modal);
const MotionModalOverlay = motion.create(ModalOverlay);
```

This enables using props like [animate](https://motion.dev/docs/react-motion-component#animation) with React Aria Components.

```tsx
<MotionModal
  initial={{opacity: 0}}
  animate={{opacity: 1}}>
  {/* ... */}
</MotionModal>
```

Overlay exit animations can be implemented using the `isExiting` prop, which keeps the element in the DOM until an animation is complete. Motion's [variants](https://motion.dev/docs/react-motion-component#variants) are a good way to setup named animation states.

```tsx
type AnimationState = 'unmounted' | 'hidden' | 'visible';

function Example() {
  /*- begin highlight -*/
  // Track animation state.
  let [animation, setAnimation] = React.useState<AnimationState>('unmounted');
  /*- end highlight -*/

  return (
    <DialogTrigger
      /*- begin highlight -*/
      // Start animation when open state changes.
      onOpenChange={isOpen => setAnimation(isOpen ? 'visible' : 'hidden')}
      /*- end highlight -*/
    >
      <Button>Open dialog</Button>
      <MotionModalOverlay
        /*- begin highlight -*/
        // Prevent modal from unmounting during animation.
        isExiting={animation === 'hidden'}
        // Reset animation state once it is complete.
        onAnimationComplete={animation => {
          setAnimation(a => animation === 'hidden' && a === 'hidden' ? 'unmounted' : a)
        }}
        /*- end highlight -*/
        variants={{
          hidden: {opacity: 0},
          visible: {opacity: 1}
        }}
        initial="hidden"
        animate={animation}>
        <MotionModal
          variants={{
            hidden: {opacity: 0, y: 32},
            visible: {opacity: 1, y: 0}
          }}>
          {/* ... */}
        </MotionModal>
      </MotionModalOverlay>
    </DialogTrigger>
  );
}
```

The [AnimatePresence](https://motion.dev/docs/react-animate-presence) component allows you to animate when items are added or removed in collection components. Use `array.map` to create children, and make sure each child has a unique `key` in addition to an `id` to ensure Motion can track it.

```tsx
import {GridList, GridListItem} from 'react-aria-components';
import {motion, AnimatePresence} from 'motion/react';

const MotionItem = motion.create(GridListItem);

<GridList>
  <AnimatePresence>
    {items.map(item => (
      <MotionItem
        key={item.id}
        id={item.id}
        layout
        exit={{opacity: 0}}>
        {/* ... */}
      </MotionItem>
    ))}
  </AnimatePresence>
</GridList>
```
